#ifndef TILEMAP_H
#define TILEMAP_H
// stl
#include <string>
#include <sstream>
#include <iostream>
#include <assert.h>
#include <vector>
#include <unordered_map>
#include <fstream>
#include <functional>
#include <map>
#include <utility>
// #include <cmath>
#include <optional>
#include <algorithm>
#include <atomic>
#include <thread>
#include <stdexcept>
// raylib
#include <raylib.h>
#include <raymath.h>
// maze
#include <mapbase.h>
#include <tilemapgenerator.h>
#include <raycast.h>

class TileMap
{
private:
    int width, height;
    int blockSize = 48;
    Coord<int> startPoint;
    Coord<int> endPoint;
    std::vector<std::vector<int>> map;
    std::unordered_map<int, Texture2D> textures;
    
    bool isInited = false;
    std::thread* search_thread = nullptr;
    std::atomic<bool> algorithm_interrupt{false};


    // shader_walldark
    Shader shader_walldark;
    int shader_walldark_intensity_local;
    int shader_walldark_direction_local;
    int shader_walldark_diagDirection_local;
    float shader_walldark_intensity = 0.3;
    Vector4 shader_walldark_direction;
    Vector4 shader_walldark_diagDirection;

    // shader_mask
    Image mask_image = GenImageColor(10 * 48, 10* 48, Color{0,0,0,0});
    Texture2D mask_texture;
    Shader maskShader;
    int pointCount = 32;
    int mask_resolutionLoc;
    int mask_pointCountLoc;

    struct BlockQuantities
    {
        int end = 0;
        int start = 0;
        int floor = 0;
        int wall = 0;
        int grass = 0;
        int lava = 0;
        void reset(){end=0;start=0;floor=0;wall=0;grass=0;lava=0;}
        void quantiAddOne(int& value){
            if(value == Blocks::value::start) start++;
            else if(value == Blocks::value::end) end++;
            else if(value == Blocks::value::floor) floor++;
            else if(value == Blocks::value::grass) grass++;
            else if(value == Blocks::value::wall) wall++;
            else if(value == Blocks::value::lava) lava++;
        }
    } block_quantities;

    enum State{working, finished};
    State search_state = State::finished;

    // Ploy
    // RayCast raycaster;

public:
    bool fogMode=false;
    std::map<std::pair<int,int>,bool> visibleBlocks;

    Camera2D camera = {0};
    Camera2D camera_cast;
    int currentPath = 0;
    std::vector<std::vector<Coord<int>>> path;
    std::vector<std::pair<Vector2, Vector2>> walls;  // 墙壁线段

    // Ploy
    Vector2 curser;
    RayCast raycaster;
    bool first = true;
    RenderTexture2D renderTexture = LoadRenderTexture(48*10,48*10);
public:
    TileMap() {
        camera.target = {0,0};
        camera.offset = {0,0};
        camera.rotation = 0.0f;
        camera.zoom = 3.0f;

        camera_cast.target = {0,0};
        camera_cast.offset = {0,0};
        camera_cast.rotation = 0;
        camera_cast.zoom = 48;

        shader_walldark = LoadShader(0, "assets\\shaders\\walldark2.frag");
        shader_walldark_intensity_local = GetShaderLocation(shader_walldark, "intensity");
        shader_walldark_direction_local = GetShaderLocation(shader_walldark, "direction");
        shader_walldark_diagDirection_local = GetShaderLocation(shader_walldark, "diagDirection");

        mask_texture = LoadTextureFromImage(mask_image);
        maskShader = LoadShader(0,"assets/shaders/mask.fs");
        mask_resolutionLoc = GetShaderLocation(maskShader, "resolution");
        mask_pointCountLoc = GetShaderLocation(maskShader, "pointCount");
        float resolution[2] = { (float)10*48, (float)10*48 };
        SetShaderValue(maskShader, mask_resolutionLoc, resolution, SHADER_UNIFORM_VEC2);
        // 设置顶点数量
        SetShaderValue(maskShader, mask_pointCountLoc, &pointCount, SHADER_UNIFORM_INT);
    }

    void initTextures(std::unordered_map<std::string, Texture2D>& texture_blocks){
        blockSize = texture_blocks["end"].width;
        textures.emplace(-2,texture_blocks["end"]);
        textures.emplace(-1,texture_blocks["start"]);
        textures.emplace(0,texture_blocks["floor"]);
        textures.emplace(1,texture_blocks["wall"]);
        textures.emplace(2,texture_blocks["grass"]);
        textures.emplace(3,texture_blocks["lava"]);
    }

    bool search(std::function<std::vector<std::vector<Coord<int>>>(std::vector<std::vector<int>>,Coord<int>,Coord<int>, const std::atomic<bool>&)> algo)
    {
        if(block_quantities.start != 1 || block_quantities.end != 1)
        {
            std::cout << "zxionf: TileMap block_quantities.start != 1 || block_quantities.end != 1" << std::endl;
            return false;
        }
        if(isInited)for(int i=0; i<width; i++)
            for(int j=0; j<height; j++)
            {
                if(map[i][j]==Blocks::value::start) startPoint = {i ,j};
                else if(map[i][j]==Blocks::value::end) endPoint = {i, j};
            }
        currentPath = 0;
        algorithm_interrupt = true;
        if (search_thread!=nullptr && search_thread->joinable()) {
            search_thread->join();
            delete search_thread;
        }
        algorithm_interrupt = false;
        search_state = State::working;
        search_thread = new std::thread([this, algo](){
            // path = algo(map,startPoint,endPoint,algorithm_interrupt);
            try {
                path = algo(map,startPoint,endPoint,algorithm_interrupt);
                search_state = State::finished;
            } catch (const std::runtime_error& e) {
                std::cout << "zxionf:Algorithm was interrupted: " << e.what() << std::endl;
            } catch (...) {
                std::cout << "zxionf:Algorithm encountered an error" << std::endl;
            }
        });
        return true;
    }

    void load(const TileMapGenerator& gener){
        if(gener.getWidth()<2) return;
        if(gener.getHeight()<2) return;
        this->map.clear();
        this->walls.clear();
        block_quantities.reset();
        this->map = gener.getMap();
        width = gener.getWidth();
        height = gener.getHeight();
        isInited = true;
        for(int i=0; i<width; i++)
            for(int j=0; j<height; j++){
                if(map[i][j] == Blocks::value::start) startPoint = {i,j};
                else if(map[i][j] == Blocks::value::end) endPoint = {i,j};
                block_quantities.quantiAddOne(map[i][j]);
            }
        for(int j=0; j<height; j++){
            for(int i=0; i<width; i++)
            std::cout << map[i][j] << " ";
            std::cout << std::endl;
        }
        std::cout << "zxionf:map size " << map.size() << " x " << map[0].size() << std::endl;
        mergeWalls();
    }

    void load(const std::string& path){
        assert(FileExists(path.c_str()));
        map.clear();
        block_quantities.reset();
        char* raw = LoadFileText(path.c_str());
        std::stringstream text(raw);
        std::string line;
        std::getline(text, line);
        std::stringstream linestream(line);

        linestream >> width >> height;
        // 读取地图数据
        for (int y = 0; y < height; y++) {
            if (std::getline(text, line)) {
                std::stringstream lineStream(line);
                std::vector<int> row;
                int value;
                
                for (int x = 0; x < width; x++){
                    lineStream >> value;
                    if(value == Blocks::value::start) startPoint = {x,y};
                    else if(value == Blocks::value::end) endPoint = {x,y};
                    block_quantities.quantiAddOne(value);
                    row.push_back(value);
                }
                map.push_back(row);
            }
        }
    
        // 创建转置后的矩阵：cols x rows
        std::vector<std::vector<int>> result(width, std::vector<int>(height));
    
        for (size_t i = 0; i < height; i++)
            for (size_t j = 0; j < width; j++)
                result[j][i] = map[i][j];
        map.clear();
        map = std::move(result);

        UnloadFileText(raw);

        isInited = true;

        for(int i=0; i<width; i++){
            for(int j=0; j<height; j++){
                visibleBlocks.emplace(std::make_pair(i,j),true);
                // visibleBlocks[std::make_pair(i, j)] = false;
                std::cout << map[i][j] << " ";
            }
            std::cout << std::endl;
        }

        setFog(fogMode);
        std::cout << "zxionf:map size " << map.size() << " x " << map[0].size() << std::endl;

        // 融并所有边
        mergeWalls();
    }

    void setFogMode(bool flag)
    {
        fogMode=flag;
    }
    void setFog(bool flag)
    {
        if(flag)
        {
            for(int i=0; i<width; i++){
                for(int j=0; j<height; j++){
                    this->visibleBlocks[std::make_pair(i,j)]=false;
                }
            }
        }
    }

    void save(const std::string& path){
        if (map.empty()) return;
        std::ofstream file(path);
        if (!file.is_open())return;

        file << width << " " << height << std::endl;
    
        for (int y = 0; y < height; y++) {
            for (int x = 0; x < width; x++) {
                file << map[x][y];
                if (x < width - 1) file << " ";
            }
            file << std::endl;
        }
        file.close();
        std::cout << "[] map saved at " << path << std::endl;
    }

    void switchMap(){}

    void drawInfo(int x, int y, int fontSize, Color color){
        DrawText(TextFormat("MapSize : %d x %d", width, height), x, y, fontSize, color);
        DrawText(TextFormat("PossibleStartPoint : %d , %d", startPoint.x, startPoint.y), x, y+fontSize, fontSize, color);
        DrawText(TextFormat("PossibleEndPoint : %d , %d", endPoint.x, endPoint.y), x, y+fontSize*2, fontSize, color);
        DrawText(TextFormat("BlockQuantities : %d %d %d %d %d %d", block_quantities.end, block_quantities.start, block_quantities.floor, block_quantities.wall, block_quantities.grass, block_quantities.lava), x, y+fontSize*3, fontSize, color);
        DrawText(TextFormat("CameraZoom : %f", camera.zoom), x, y+fontSize*4, fontSize, color);
        DrawText(TextFormat("TotalPath : %d", path.size()), x, y+fontSize*5, fontSize, color);
        DrawText(TextFormat("CurrentPathID : %d", currentPath), x, y+fontSize*6, fontSize, color);
        DrawText(TextFormat("TotalMergedWalls : %d", walls.size()), x, y+fontSize*7, fontSize, color);
        DrawText(TextFormat("SearchState : %s", search_state==State::finished?"finished":"searching"), x, y+fontSize*8, fontSize, color);

    }

    void setBlock(int x, int y, int block) {
        if(x> 0 && x < width-1 && y > 0 && y< height-1)
        {
            map[x][y] = block;
            block_quantities.reset();
            if(isInited)for(int i=0; i<width; i++)
                for(int j=0; j<height; j++)
                    block_quantities.quantiAddOne(map[i][j]);
        }
        walls.clear();
        mergeWalls();
    }

    void getShadowInfo(int x, int y)
    {
         // 正交方向
        float left=0,right=0,up=0,down=0;
        if(map[x][y] == Blocks::value::wall){
            shader_walldark_direction = {0,0,0,0};
            return;
        }
        if(map[x-1][y] == Blocks::value::wall) left = 1;
        if(map[x+1][y] == Blocks::value::wall) right=1;
        if(map[x][y-1] == Blocks::value::wall) up=1;
        if(map[x][y+1] == Blocks::value::wall) down=1;

        shader_walldark_direction={left,right,up,down};

        // 斜角方向
        float leftup=0, rightup=0, leftdown=0, rightdown=0;
        if(map[x-1][y-1] == Blocks::value::wall) leftup = 1;
        if(map[x+1][y-1] == Blocks::value::wall) rightup=1;
        if(map[x-1][y+1] == Blocks::value::wall) leftdown=1;
        if(map[x+1][y+1] == Blocks::value::wall) rightdown=1;
        shader_walldark_diagDirection={leftup,rightup,leftdown,rightdown};
    }

    void render(){
        if (IsKeyDown(KEY_RIGHT)) camera.target.x --;
        if (IsKeyDown(KEY_LEFT)) camera.target.x ++;
        if (IsKeyDown(KEY_UP)) camera.target.y ++;
        if (IsKeyDown(KEY_DOWN)) camera.target.y --;

        if (IsKeyDown(KEY_Z)) camera.zoom -= 0.01;
        if (IsKeyDown(KEY_X)) camera.zoom += 0.01;

        if(IsKeyPressed(KEY_E)){
            // if(currentPath == path.size()) currentPath=0;
            if(currentPath < path.size()) currentPath=(currentPath+1)%path.size();
        }

        BeginMode2D(camera);
        // // 绘制地图
        if(isInited)for(int i=0; i<width; i++)
            for(int j=0; j<height; j++)
            {
                // 应用着色器
                BeginShaderMode(shader_walldark);
                float me = map[i][j] == Blocks::value::wall;
                SetShaderValue(shader_walldark, GetShaderLocation(shader_walldark, "me"), &me, SHADER_UNIFORM_FLOAT);
                getShadowInfo(i,j);
                SetShaderValue(shader_walldark, shader_walldark_intensity_local, &shader_walldark_intensity, SHADER_UNIFORM_FLOAT);
                SetShaderValue(shader_walldark, shader_walldark_direction_local, &shader_walldark_direction, SHADER_UNIFORM_VEC4);
                SetShaderValue(shader_walldark, shader_walldark_diagDirection_local, &shader_walldark_diagDirection, SHADER_UNIFORM_VEC4);
                // 绘制地图块
                if(visibleBlocks[std::make_pair(i,j)])
                    DrawTextureEx(textures[map[i][j]],{(float)i*48, (float)j*48},0,48/blockSize,WHITE);
                EndShaderMode();
            }
        
        // 绘制搜索到的路径
        if(!path.empty())
            for(int i=0; i<path[currentPath].size()-1; i++)
                // DrawCircle(path[currentPath][i].first*48+24,path[currentPath][i].second*48+24,12,RED);
                DrawLineEx({(float)path[currentPath][i].x*48+24,(float)path[currentPath][i].y*48+24},
                    {(float)path[currentPath][i+1].x*48+24,(float)path[currentPath][i+1].y*48+24},
                    4,RED);

        for (auto& wall : walls) {
            DrawLineV(Vector2Scale(wall.first, 48), Vector2Scale(wall.second, 48), WHITE);
        }
        EndMode2D();

        curser = {(float)GetMouseX() / 48 /3 ,(float)GetMouseY()/3/48};
        // curser = {0,0};
        if(first){
            raycaster.cast(Vector2{0,0});
            first = false;
        }
        if(fogMode)
        {
            BeginMode2D(camera_cast);
            raycaster.cast(curser);
            
            // DrawTriangleFan(raycaster.polygonPoints.data() , raycaster.polygonPoints.size(), Color{20,120,0,255});
            // for(int i=0; i<raycaster.polygonPoints.size(); i++)
            // {
            //     DrawLineV(curser, Vector2Scale(raycaster.polygonPoints[i],1), WHITE);
            // }
            EndMode2D();

            BeginTextureMode(renderTexture);
                ClearBackground(BLACK);
                BeginMode2D(camera_cast);
                DrawTriangleFan(raycaster.polygonPoints.data() , raycaster.polygonPoints.size(), WHITE);
                // DrawCircleGradient(curser.x,curser.y,2,WHITE,BLACK);
                EndMode2D();
            EndTextureMode();
            
            BeginBlendMode(BLEND_MULTIPLIED);
                DrawTexturePro(renderTexture.texture, {0, 0, (float)renderTexture.texture.width, -(float)renderTexture.texture.height},{0, 0, 48 * 30, 48 *30}, {0,0}, 0, WHITE);
            EndBlendMode();

            // for (auto& wall : raycaster.walls)
            //     DrawLineV(Vector2Scale(wall.first,48*3), Vector2Scale(wall.second,48*3), GREEN);
            for(auto& i : raycaster.usefulIntersection) if(i!=nullptr) delete i;
                raycaster.usefulIntersection.clear();
        }
        
    }

    std::vector<int>& operator[](int index){
        return map[index];
    }

    bool getSearchFinished(){
        return search_state == State::finished;
    } 

    Coord<int> getStartPoint(){
        return startPoint;
    }

    Coord<int> getEndPoint(){
        return endPoint;
    }

    std::vector<std::vector<Coord<int>>> getPath()
    {
        return path;
    }

    std::vector<std::vector<int>> getMap(){
        return map;
    }
    int getWidth() {return width;}
    int getHeight() {return height;}

    void mergeWalls()
    {
        // 添加边
        if (isInited)
            for (int i = 0; i < width; i++)
                for (int j = 0; j < height; j++)
                {
                    if (map[i][j] == Blocks::value::wall && !((i == 0) || i == width - 1 || j == 0 || j == height - 1))
                    {
                        walls.push_back({{(float)i, (float)j}, {(float)i + 1, (float)j}});
                        walls.push_back({{(float)i, (float)j}, {(float)i, (float)j + 1}});
                        walls.push_back({{(float)i + 1, (float)j}, {(float)i + 1, (float)j + 1}});
                        walls.push_back({{(float)i, (float)j + 1}, {(float)i + 1, (float)j + 1}});
                    }
                }
        for (int i = 1; i < width - 1; i++)
        {
            walls.push_back({{(float)i, 1}, {(float)i + 1, 1}});
            walls.push_back({{(float)i, (float)height - 1}, {(float)i + 1, (float)height - 1}});
        }
        for (int i = 1; i < height - 1; i++)
        {
            walls.push_back({{1, (float)i}, {1, (float)i + 1}});
            walls.push_back({{
                                 (float)width - 1,
                                 (float)i,
                             },
                             {(float)width - 1, (float)i + 1}});
        }
        // 去重与融并边
        // 去重
        bool changed;
        do
        {
            changed = false;
            for (auto wall = walls.begin(); wall != walls.end(); ++wall)
            {
                for (auto w = std::next(wall); w != walls.end();)
                {
                    // 正确的条件判断：检查两条边是否完全相同（包括方向相同或相反）
                    if (
                        // 方向相同的情况
                        (wall->first.x == w->first.x && wall->first.y == w->first.y &&
                         wall->second.x == w->second.x && wall->second.y == w->second.y) ||
                        // 方向相反的情况
                        (wall->first.x == w->second.x && wall->first.y == w->second.y &&
                         wall->second.x == w->first.x && wall->second.y == w->first.y))
                    {
                        // 先删除w，再删除wall
                        w = walls.erase(w);
                        wall = walls.erase(wall);
                        changed = true;
                        break; // 跳出内层循环
                    }
                    else
                    {
                        ++w;
                    }
                }
                if (changed)
                    break; // 如果删除了元素，重新开始外层循环
            }
        } while (changed);

        // 融并边
        // 融并相邻边
        bool merged;
        do
        {
            merged = false;

            // 分离水平边和垂直边
            std::vector<std::pair<Vector2, Vector2>> horizontalEdges;
            std::vector<std::pair<Vector2, Vector2>> verticalEdges;

            for (const auto &edge : walls)
            {
                if (edge.first.y == edge.second.y)
                {
                    // 水平边
                    horizontalEdges.push_back(edge);
                }
                else if (edge.first.x == edge.second.x)
                {
                    // 垂直边
                    verticalEdges.push_back(edge);
                }
            }

            // 合并水平边
            for (auto it = horizontalEdges.begin(); it != horizontalEdges.end();)
            {
                bool foundMerge = false;

                for (auto other = horizontalEdges.begin(); other != horizontalEdges.end();)
                {
                    if (it == other)
                    {
                        ++other;
                        continue;
                    }

                    // 检查是否在同一水平线上
                    if (it->first.y == other->first.y)
                    {
                        // 检查是否相邻
                        if (it->second.x == other->first.x)
                        {
                            // it的右端点连接other的左端点
                            it->second = other->second;
                            other = horizontalEdges.erase(other);
                            foundMerge = true;
                            merged = true;
                        }
                        else if (it->first.x == other->second.x)
                        {
                            // it的左端点连接other的右端点
                            it->first = other->first;
                            other = horizontalEdges.erase(other);
                            foundMerge = true;
                            merged = true;
                        }
                        else
                        {
                            ++other;
                        }
                    }
                    else
                    {
                        ++other;
                    }
                }

                if (foundMerge)
                {
                    // 如果合并了，重新开始检查
                    it = horizontalEdges.begin();
                }
                else
                {
                    ++it;
                }
            }

            // 合并垂直边
            for (auto it = verticalEdges.begin(); it != verticalEdges.end();)
            {
                bool foundMerge = false;

                for (auto other = verticalEdges.begin(); other != verticalEdges.end();)
                {
                    if (it == other)
                    {
                        ++other;
                        continue;
                    }

                    // 检查是否在同一垂直线上
                    if (it->first.x == other->first.x)
                    {
                        // 检查是否相邻
                        if (it->second.y == other->first.y)
                        {
                            // it的下端点连接other的上端点
                            it->second = other->second;
                            other = verticalEdges.erase(other);
                            foundMerge = true;
                            merged = true;
                        }
                        else if (it->first.y == other->second.y)
                        {
                            // it的上端点连接other的下端点
                            it->first = other->first;
                            other = verticalEdges.erase(other);
                            foundMerge = true;
                            merged = true;
                        }
                        else
                        {
                            ++other;
                        }
                    }
                    else
                    {
                        ++other;
                    }
                }

                if (foundMerge)
                {
                    // 如果合并了，重新开始检查
                    it = verticalEdges.begin();
                }
                else
                {
                    ++it;
                }
            }

            // 重新组合边
            walls.clear();
            walls.insert(walls.end(), horizontalEdges.begin(), horizontalEdges.end());
            walls.insert(walls.end(), verticalEdges.begin(), verticalEdges.end());

        } while (merged);
    }
};

#endif